#-*-coding:utf8-*-
#!/usr/bin/python

import pygtk
pygtk.require("2.0")
import gtk
import tag
import scan
import gobject
import shutil
import os
import time
import player
import cache_message
import sort_message
from player import *
from kirbybase import KirbyBase, KBError


player = Player()

gobject.threads_init()
		
class Principal:
    """
    Classe construisant l'interface, main du lecteur
    """

    def __init__(self):
        """
        Crée la fenêtre principale et ses éléments
        """ 
                        
        settings = gtk.settings_get_default()
        settings.props.gtk_button_images = True
        
            # crée la fenêtre principale 
        mainWindow = gtk.Window()
        mainWindow.set_title("Gestion des tags mp3")
        mainWindow.resize(850,600)
        mainWindow.set_resizable(True)
        mainWindow.connect("destroy", self.on_mainWindow_destroy)
        
            # crée la HBox contenant le tout
        self.HBox_princ = gtk.HPaned()
        
            # crée la VBox de gauche, contenant la navigation dans la bibliothèque
        self.VBox_gch = gtk.VBox(False)
        self.VBox_gch.set_size_request(150, 0)
        
        
            # crée la VBox de droite, contenant la navigation courante, la lecture et les options
        self.VBox_dte = gtk.VBox(False)
        
            # crée la HBox contenant les options
        self.HBox_opt = gtk.HBox(False)
        
            #crée la HBox contenant le lecteur
        self.HBox_lect = gtk.HBox(False)
        
            # crée le curseur de lecture
        self.moveCurs = False
        self.oldAdjstNow = 0
        self.adjust = gtk.Adjustment(0, 0, 0, 1)
        self.curseur = gtk.HScale(self.adjust)
        self.curseur.set_draw_value(False)
        self.curseur.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
        self.cursM = self.curseur.connect("value_changed", self.getCurs)
        
        source = gobject.timeout_add(10, self.majCurs)
        
		# crée le bouton de parcours 
        self.parcButton = gtk.Button("Parcourir")
        self.parcButton.connect("clicked", self.on_parcButton_clicked)
        
		# crée le bouton d'organisation automatique
        self.TagButton = gtk.Button("Organisation automatique")
        self.TagButton.connect("clicked", self.on_TagButton_clicked)

        # crée le bouton pour cacher un message 
        self.CacheButton = gtk.Button("Cacher un message")
        self.CacheButton.connect("clicked", self.on_CacheButton_clicked)

        # crée le bouton pour retrouver un message
        self.TrouveButton = gtk.Button("Retrouver un message")
        self.TrouveButton.connect("clicked", self.on_TrouveButton_clicked)

        # crée le bouton pour ajouter tout le liststore à la liste de lecture
        self.ListeButton = gtk.Button("Tout ajouter")
        self.ListeButton.connect("clicked", self.on_ListeButton_clicked)
            
		#crée le bouton play
        self.playButton = gtk.Button(label=None, stock="gtk-media-play")
        self.playButton.connect("clicked", self.on_playButton_clicked)
        
		#crée le bouton pause
        
        self.pauseButton = gtk.Button(label=None, stock="gtk-media-pause")
        self.pauseButton.connect("clicked", self.on_pauseButton_clicked)
        
		#crée le bouton stop
        self.stopButton = gtk.Button(label=None, stock="gtk-media-stop")
        self.stopButton.connect("clicked", self.on_stopButton_clicked)
        
		#crée le bouton suivant
        self.suivButton = gtk.Button(label=None, stock="gtk-media-next")
        self.suivButton.connect("clicked", self.on_suivButton_clicked)
        
		#crée le bouton précédent
        self.precButton = gtk.Button(label=None, stock="gtk-media-previous")
        self.precButton.connect("clicked", self.on_precButton_clicked)
        
    	#crée le curseur de volume
        volAdjust = gtk.Adjustment(1, 0, 1, 0.05)
        self.volCurs = gtk.HScale(volAdjust)
        self.volCurs.set_draw_value(False)
        self.volCurs.connect("value_changed", self.getVolCurs)
        self.volCurs.set_size_request(50, 0)
        
    	#crée le label de titre
        self.titleLabel = gtk.Label("")

	    #crée le label temps
        self.secC = None
        self.timeLabel = gtk.Label("--.--/--.--")
        
        #crée la liste de droite
        self.liststore_cour = gtk.ListStore(str,str,str,str,str,str)
        
        
        #crée l'arbre lié à la liste
        self.create_Tree(self.liststore_cour)
        
	    #scan de la bibliothèque
        bdd = scan.scan()
        
        #liste contenant la bdd
        self.liste = bdd.range_lst()
        
        scroll1 = gtk.ScrolledWindow(None , None)
        scroll1.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scroll1.add_with_viewport(self.treeview)
        scroll1.show()

        treestore = gtk.TreeStore(str)
        treeview_gch = gtk.TreeView(treestore)
        cell = gtk.CellRendererText()
        tvcolumn = gtk.TreeViewColumn('Bibliothèque', cell)
        
        
        iter_bibli = treestore.append(None, ["Bibliothèque"])
        iter_art = treestore.append(iter_bibli, ["Artiste"])
        iter_alb = treestore.append(iter_bibli, ["Album"])
        iter_gen = treestore.append(iter_bibli, ["Genre"])
        iter_dat = treestore.append(iter_bibli, ["Date"])
        
        iter_play = treestore.append(None, ["Listes de lecture"])
        iter_cour = treestore.append(iter_play, ["Liste courante"])
        tvcolumn.add_attribute(cell, 'text', 0)
        treeview_gch.append_column(tvcolumn)
            
        self.remplit(iter_art, treestore, 1)
        self.remplit(iter_alb, treestore, 2)
        self.remplit(iter_gen, treestore, 3)
        self.remplit(iter_dat, treestore, 4)
            
            
        scroll = gtk.ScrolledWindow(None , None)
        scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scroll.add_with_viewport(treeview_gch)
        scroll.show()
        
        treeview_gch.connect( 'row-activated' , self.on_tree_clicked, treestore)

        self.VBox_gch.pack_start(scroll, True, True, 0)
        

            
        self.HBox_opt.set_usize(mainWindow.get_size()[0]/5,mainWindow.get_size()[1]/10)
        self.HBox_lect.set_usize(mainWindow.get_size()[0]/8, mainWindow.get_size()[1]/10)
            
        
        self.HBox_opt.pack_start(self.parcButton, False, False, 0)
        self.HBox_opt.pack_start(self.TagButton, False, False, 0)
        self.HBox_opt.pack_start(self.CacheButton, False, False, 0)
        self.HBox_opt.pack_start(self.TrouveButton, False, False, 0)
        self.HBox_opt.pack_start(self.ListeButton, False, False, 0)

        
        self.HBox_lect.pack_start(self.timeLabel, False, False, 0)
        self.HBox_lect.pack_end(self.volCurs, False, False, 0)
        self.HBox_lect.pack_end(self.suivButton, False, False, 0)
        self.HBox_lect.pack_end(self.stopButton, False, False, 0)
        self.HBox_lect.pack_end(self.precButton, False, False, 0)
        self.HBox_lect.pack_end(self.pauseButton, False, False, 0)
        self.HBox_lect.pack_end(self.playButton, False, False, 0)
        self.HBox_lect.pack_end(self.titleLabel, False, False, 0)

        
        self.VBox_dte.pack_start(scroll1, True, True, 0)
        
        self.VBox_dte.pack_start(self.HBox_opt, False, False, 0)
        self.VBox_dte.pack_start(self.curseur, False, False, 5)
        self.VBox_dte.pack_start(self.HBox_lect, False, False, 0)
        
        self.HBox_princ.add1(self.VBox_gch)
        self.HBox_princ.add2(self.VBox_dte)
            
        self.timeLabel.set_text("00:00/00:00")
            
        mainWindow.add(self.HBox_princ)
        mainWindow.show_all()
        
		
    def showTimePlayer(self, tps, tpsT) :
        """
        maj le texte du label de temps
        """
        affMin = tps / 60
        affSec = tps % 60
        if affSec in range(9):
            affSec = "0" + str(affSec)
        affMinT = tpsT / 60
        affSecT = tpsT % 60
        if affSecT in range(9):
            affSecT = "0" + str(affSecT)
        self.timeLabel.set_text(str(affMin) + ":"+ str(affSec)+"/" + str(affMinT)+":"+str(affSecT))


      
    def on_tree_clicked(self, widget, cell, chemin, treestore):
        """
        Réagit au clic sur l'arbre de gauche:
        Si un item de recherche, ajoute dans la liste de droite tous les titres correspondants.
        """
        self.liststore_cour.clear()

        for x in self.liste:
            # doublon évite le cas ou deux attributs d'un titre soit égaux et qu'il y ait plusieurs ajouts
            doublon = False
            for y in x:
                if str(treestore[cell][0]) == str(y) and doublon == False:
                    self.liststore_cour.append([x[0], x[1], x[2], x[3], x[5], x[5]])
                    doublon = True
                elif str(treestore[cell][0]) == "Liste courante":
                    liste_lecture = player.getPlaylist()
                    for i in liste_lecture:
                        if i == y:
                            self.liststore_cour.append([x[0], x[1], x[2], x[3], x[5], x[5]])


            
    def remplit(self, iterateur, treestore, champ):
        """
        ajoute dans le modèle treestore l'itérateur iterateur. L'attribut champ permet de préciser l'index de self.liste à ajouter (par exemple, 0 signifie tous les titres)
        """
        lst = []
        for x in self.liste:
            lst.append(x[champ])
            
        lst.sort()
        
        liste_tri=[]
        for x in lst:
            if x not in liste_tri:
                liste_tri.append(x)
            
        for x in liste_tri:
            iter_cour = treestore.append(iterateur, [x])                
        
    def showTitlePlayer(self):
        chemin = player.getCurrentSongPath()
        for x in self.liste:
            if x[5] == chemin:
                self.titleLabel.set_text(str(x[0])+" - "+str(x[1]))
        

        
    def majCurs(self):
        """
        appelle setCurs qui met à jour le curseur de lecture et appelle showTimePlayer pour la mise à jour du compteur de lecture
        """
        try:
            tps = int(player.getPlayedDuration())
            tpsT = int(player.getCurrentSongDuration())
        except :
            tps = 0
            tpsT = 0
            pasMaj = True
            
        self.setCurs( tps, tpsT )
        self.showTimePlayer( tps, tpsT)
        self.showTitlePlayer()
        
        return True

		
    def setCurs(self, tNow, tMax = -1) :
        """
        met à jour le curseur de lecture selon tNow la position du curseur et tMax la position maximale possible.
        """
        if self.oldAdjstNow != self.adjust.get_value() or self.moveCurs :
            return
        if tMax == -1 :
            tMax = self.adjust.get_upper()

        if tMax < 0 :
            raise negativeCursUpper("Le maximum du curseur ne peut etre negatif")
        if tNow > tMax :
            raise cursOverUpper("Le curseur ne peut aller au dela du maximum")

        self.curseur.handler_block(self.cursM)
        self.adjust.set_upper(tMax)
        self.adjust.set_value(tNow)
        self.curseur.handler_unblock(self.cursM)
        self.oldAdjstNow = tNow
        
    def on_TrouveButton_clicked(self, widget):
        """
        Ouvre un explorateur pour que l'utilisateur entre sa clé pour retrouver un message
        """
        (modele, listechemins) = self.treeselection.get_selected_rows()
        for (x,) in listechemins:
            cheminFich = self.liststore_cour[x][4]
            cheminCle = '.'.join(cheminFich.split('.')[:-1])
            cheminCle += ".cle" 
            sort = sort_message.SortMessage(cheminFich, cheminCle)
            gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_NONE, message_format=sort).run()

    def on_CacheButton_clicked(self, widget):
        """
        Ouvre une fenêtre pour que l'utilisateur entre un message à cacher.
        Crée ensuite automatiquement un fichier clé pour el décodage.
        """
        messageWindow = gtk.Window()
        messageWindow.set_title("Cacher un message dans un titre")		
        messageWindow.show()
        
        VBox_message = gtk.VBox()

        label1 = gtk.Label("Entrez le message à cacher dans le titre sélectionné :")

        message = gtk.Entry()
        
        ValMessButton = gtk.Button("Valider")
        ValMessButton.connect("clicked", self.on_ValMessButton_clicked, message)
        
        VBox_message.pack_start(label1)
        VBox_message.pack_start(message)
        VBox_message.pack_start(ValMessButton)
        
        messageWindow.add(VBox_message)
        messageWindow.show_all()

    def on_ValMessButton_clicked(self, widget, widgetMessage):
    	message = widgetMessage.get_text()
        selec = self.treeview.get_selection()
        (modele, listechemins) = self.treeselection.get_selected_rows()
        liste=[]
        for (x,) in listechemins:
            chemin = self.liststore_cour[x][4]
            print chemin, message
            cache = cache_message.CacheMessage(chemin, message)

    def on_ListeButton_clicked(self, widget):
        """
        Ajoute tous les titres présent dans la listestore de droite à la liste de lecture.
        """
        return

    def on_TagButton_clicked(self, widget):
        """
        Crée la fenêtre déclenchée par le bouton organisation automatique
        """

        tagWindow = gtk.Window()
        tagWindow.set_title("Organisation automatique")		
        tagWindow.show()
        
        VBox_tag = gtk.VBox()

        label1 = gtk.Label("Veuillez entrer le chemin pour l'organisation automatique:")
        label2 = gtk.Label("Entrez la catégorie entre slash, par exemple: /genre/artiste/titre - artiste")

        self.chemin_tag = gtk.Entry()
        
        validButton = gtk.Button("Valider")
        validButton.connect("clicked", self.on_valid_tagButton_clicked, tagWindow)
        
        VBox_tag.pack_start(label1)
        VBox_tag.pack_start(self.chemin_tag)
        VBox_tag.pack_start(label2)
        VBox_tag.pack_start(validButton)
        
        tagWindow.add(VBox_tag)
        tagWindow.show_all()
	
    
        
    def on_valid_tagButton_clicked(self, widget, window):
        """
        Crée la fenêtre de confirmation de réoganisation et appelle la fonction de réorganisation
        """
        
        confirm = gtk.MessageDialog(window, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK_CANCEL, "Voulez vous vraiment réorganiser votre bibliothèque ?")
        result = confirm.run()

        if(result == gtk.RESPONSE_OK):
            self.reorg(confirm, window)
            
        confirm.destroy()
        window.destroy()

		
    def reorg(self, confirm, window):
        """
        Organise automatiquement les fichiers en fonction du chemin indiqué:
        -on regarde si le chemin entré par l'utilisateur contient une chaine de carac;
        -si oui on le remplace par le libellé particulier au(x) titre(s);
        -on déplace le(s) fichier(s) selon le(s) chemin(s) trouvé(s)
        """

        carac=["titre", "artiste", "album", "genre"]
		
        (modele, listechemins) = self.treeselection.get_selected_rows()
        liste=[]
        for (x,) in listechemins:
            liste.append(x)

        chemin_entre = self.chemin_tag.get_text()
        print chemin_entre
		
        liste_entre = chemin_entre.split("/")
		
        for f in liste:
            for x in liste_entre:
                for y in carac:
                    if y in x:
                        chemin_entre = chemin_entre.replace(y,(self.liststore_cour[f][carac.index(y)]))
        format = self.liststore_cour[f][4].split(".")
        chemin_entre += "." + format[-1]
        user = os.getlogin()			
        chemin_entre = "/home/" + user + "/Musique" + chemin_entre
        print chemin_entre


        try:
            os.renames( self.liststore_cour[f][4], chemin_entre )
            for x in self.liste:
                if x[5] == self.liststore_cour[f][4]:
                    x[5] = chemin_entre
                    self.liststore_cour[f][4] = chemin_entre
        except:
            print "Erreur."
            confirm.destroy()
            window.destroy()
                            

    def on_titre_clicked(self, widget, cell, chemin, liststore):
        """
        ajoute un titre à la liste de lecture du player lorsqu'une ligne du modèle est sélectionnée
        """
        print liststore[cell][4]
        player.add(liststore[cell][4])

    def create_Tree(self, modele):
        """
        Crée la vue du modèle
        """ 

        self.treeview = gtk.TreeView( modele )
        self.treeview.set_headers_clickable(True)
        self.treeview.set_reorderable(True)
        
                
        self.renderer = gtk.CellRendererText()
        self.renderer.set_property( 'editable', True )
        self.renderer.connect( 'edited', self.rappel_edited_title, modele )
        self.colonne_title = gtk.TreeViewColumn("Titre", self.renderer, text=0)
        self.treeview.append_column( self.colonne_title)
        
        self.renderer1 = gtk.CellRendererText()
        self.renderer1.set_property('editable', True )
        self.renderer1.connect( 'edited', self.rappel_edited_artist, modele )
        self.colonne_artist = gtk.TreeViewColumn("Artiste", self.renderer1, text=1)
        self.treeview.append_column( self.colonne_artist )
            
        self.renderer2 = gtk.CellRendererText()
        self.renderer2.set_property('editable', True )
        self.renderer2.connect( 'edited', self.rappel_edited_album, modele )
        self.colonne_album = gtk.TreeViewColumn("Album", self.renderer2, text=2)
        self.treeview.append_column( self.colonne_album )
            
        self.renderer3 = gtk.CellRendererText()
        self.renderer3.set_property('editable', True )
        self.renderer3.connect( 'edited', self.rappel_edited_genre, modele )
        self.colonne_genre = gtk.TreeViewColumn("Genre", self.renderer3, text=3)
        self.treeview.append_column( self.colonne_genre )
            
        self.renderer4 = gtk.CellRendererText()
        self.colonne_chemin = gtk.TreeViewColumn("Chemin", self.renderer4, text=4)
        self.treeview.append_column( self.colonne_chemin )
                
        self.treeselection = self.treeview.get_selection()
        self.treeselection.set_mode(gtk.SELECTION_MULTIPLE)
        
        self.treeview.connect( 'row-activated' , self.on_titre_clicked, modele)
            

    def rappel_edited_title(self, cell, chemin, new_tx, liste):
        """ 
        Ancre la modification du titre et enregistre dans les tags.
        new_tx correspond au texte entré dans la cellule du modèle,
        liste est la liststore de données.
        Ici seul un titre peut-être modifié par pure logique: deux morceaux n'ont pas le même titre.
        """

        liste[chemin][0] = new_tx
        fich = tag.tag(liste[chemin][4])
        fich.set_title(new_tx)
        return

    def rappel_edited_artist(self, cell, chemin, new_tx, liste):
        """
        Ancre la modification de l'artiste et l'enregistre dans le tag.
        new_tx correspond au texte entré dans la cellule du modèle,
        liste est la liststore de données.
        Plusieurs lignes peuvent avoir été sélectionnées.
        """
        (modele, listechemins) = self.treeselection.get_selected_rows()
        for (chemin,) in listechemins:
            liste[chemin][1] = new_tx
            fich = tag.tag(liste[chemin][4])
            fich.set_artist(new_tx)
        return
    
    def rappel_edited_album(self, cell, chemin, new_tx, liste):
        """
        Ancre la modification de l'album et l'enregistre dans le tag.
        new_tx correspond au texte entré dans la cellule du modèle,
        liste est la liststore de données
        """

        (modele, listechemins) = self.treeselection.get_selected_rows()
        for (chemin,) in listechemins:
            liste[chemin][2] = new_tx
            fich = tag.tag(liste[chemin][4])
            fich.set_album(new_tx)
        return

    def rappel_edited_genre(self, cell, chemin, new_tx, liste):
        """
        Ancre la modification du genre et l'enregistre dans les tags.
        new_tx est le texte entré dans la cellule du modèle,
        liste est la liststore de données
        """

        (modele, listechemins) = self.treeselection.get_selected_rows()
        for (chemin,) in listechemins:
            liste[chemin][2] = new_tx
            fich = tag.tag(liste[chemin][4])
            fich.set_genre(new_tx)
		
		

    def on_parcButton_clicked(self, widget):
        """
        Ouvre une boite de dialogue pour introduire des lignes dans la liststore de droite. 
        Permet d'ajouter des morceaux non compris dans la bibliothèque.
        """
        
        dialogue = gtk.FileChooserDialog("Sélectionner les fichiers à modifier:", None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))

        dialogue.set_select_multiple(True)
        dialogue.set_default_response(gtk.RESPONSE_OK)
        dialogue.set_current_folder("/home/pierre/Musique")
        
        filtre = gtk.FileFilter()
        filtre.set_name("Fichiers MP3")
        filtre.add_pattern("*.mp3")
        dialogue.add_filter(filtre)
        
        filtre = gtk.FileFilter()
        filtre.set_name("All files")
        filtre.add_pattern("*")
        dialogue.add_filter(filtre)
        
        reponse = dialogue.run()
        if( reponse == gtk.RESPONSE_OK ):
            print dialogue.get_filenames()
        elif( reponse == gtk.RESPONSE_CANCEL ):
            print "Sélection annulée"

        liste_chemin = dialogue.get_filenames()
        dialogue.destroy()
        
        for x in liste_chemin:
            for y in self.liste:
                if y[5] == x: 
                    self.liststore_cour.append([y[0], y[1], y[2], y[3], y[5], y[5]])

	
    def on_playButton_clicked(self, widget) :       
        player.play()
        
    def on_pauseButton_clicked(self, widget) :
        player.pause()
            
            
    def on_stopButton_clicked(self, widget) :
        """
        Appelle la fonction player.stop() pour arrêter le morceau en lecture
        """
        player.stop()
        
    def on_suivButton_clicked(self, widget) :
        """
        Appelle la fonction player.next() pour passe au morceau suivant de la liste de lecture.
        """
        player.next()
        
    def on_precButton_clicked(self, widget) :
        """
        Appelle la fonction player.previous() pour revenir au morceau suivant de la liste de lecture.
        """
        player.previous()

    def getCurs(self, widget) :
        """
        relie le curseur au morceau en cours: cela permet de bouger le curseur et d'aller à un moment donné du morceau.
        """
        self.moveCurs = True
        try:
            tps = int(widget.get_value())
            print tps
            player.goToTime(tps)
        except:
            print "pb"
        self.moveCurs = False
            
    def getVolCurs(self, widget) :
        player.setVolume(widget.get_value())

    def on_mainWindow_destroy(self, widget):
        """
        Est appelée lors de la fermeture de la fenêtre. Permet de fermer le player et de quitter l'application
        """
        player.close()
        gtk.main_quit()


if __name__ == "__main__":
	Principal()
        player.start()
	gtk.main()


class cursOverUpper(Exception):
    """
    Exception lorsque le curseur d'avancement de lecture est trop grand
    """

    def __init__(self,raison):
        self.raison = raison

    def __str__(self):
        return self.raison

class negativeCursUpper(Exception):
    """
    Exception lorsque le curseur d'avancement est négatif
    """

    def __init__(self,raison):
        self.raison = raison

    def __str__(self):
        return self.raison
	
